/******************************************************************************
 * @file     vcom_serial.c
 * @brief    NANO100 series USBD driver Sample file
 * @version  2.0.0
 * @date     17, Dec, 2014
 *
 * @note
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
 ******************************************************************************/

/*!<Includes */
#include <string.h>
#include <stdio.h>
#include "Nano100Series.h"
#include "common.h"
#include "proximity_sensor.h"

/*---------------------------------------------------------------------------------------------------------*/
/* Global variables                                                                                        */
/*---------------------------------------------------------------------------------------------------------*/

#define EM30718_I2C_ADDR				(0x48 >> 1)

#define EM30718_REG_ADDR_PID			(0x00)
#define EM30718_REG_ADDR_CONFIG			(0x01)
#define EM30718_REG_ADDR_INTERRUPT		(0x02)
#define EM30718_REG_ADDR_PS_LT			(0x03)
#define EM30718_REG_ADDR_PS_HT			(0x04)
#define EM30718_REG_ADDR_ALSIR_TH1		(0x05)
#define EM30718_REG_ADDR_ALSIR_TH2		(0x06)
#define EM30718_REG_ADDR_ALSIR_TH3		(0x07)
#define EM30718_REG_ADDR_PS_DATA		(0x08)
#define EM30718_REG_ADDR_ALSIR_DATA1	(0x09)
#define EM30718_REG_ADDR_ALSIR_DATA2	(0x0A)
#define EM30718_REG_ADDR_TEST1			(0x0E)
#define EM30718_REG_ADDR_TEST2			(0x0F)

#define EM30718_PID						(0x31)

// without glass cover
//#define EM30718_PS_LT					(10)
//#define EM30718_PS_HT					(50)

// with glass cover
#define EM30718_PS_LT					(30)
#define EM30718_PS_HT					(50)

static struct i2c_ctrl_data i2c1_ctrl_data;
static enum proximity_sensor_state state = PROXIMITY_SENSOR_STATE_LEAVE;
static int is_em30718_exist = 0;

void I2C1_IRQHandler(void)
{
    uint32_t u32Status;
	
    I2C1->INTSTS |= I2C_INTSTS_INTSTS_Msk;

    u32Status = I2C_GET_STATUS(I2C1);
    if (I2C_GET_TIMEOUT_FLAG(I2C1)) {
        I2C_ClearTimeoutFlag(I2C1);
    } else {
        if (i2c1_ctrl_data.xfer_func != NULL) {
            (i2c1_ctrl_data.xfer_func)(u32Status);
        }
    }
}

static void I2C1_MasterTx(uint32_t u32Status)
{
    if (u32Status == 0x08) {                    /* START transmitted */
        I2C_SET_DATA(I2C1, (i2c1_ctrl_data.addr << 1)); 
        I2C_SET_CONTROL_REG(I2C1, I2C_SI);
    } else if (u32Status == 0x18) {             /* SLA+W transmitted and ACK received */
        I2C_SET_DATA(I2C1, i2c1_ctrl_data.tx_data[i2c1_ctrl_data.tx_data_index++]);
        I2C_SET_CONTROL_REG(I2C1, I2C_SI);
    } else if ((u32Status == 0x20)) {           	/* SLA+W transmitted and NACK received */
        I2C_SET_CONTROL_REG(I2C1, I2C_STO | I2C_SI);
		//i2c1_ctrl_data.flag_finished = 1;
    } else if (u32Status == 0x28) {             /* DATA transmitted and ACK received */
        if (i2c1_ctrl_data.tx_data_index < i2c1_ctrl_data.tx_data_length) {
            I2C_SET_DATA(I2C1, i2c1_ctrl_data.tx_data[i2c1_ctrl_data.tx_data_index++]);
        	I2C_SET_CONTROL_REG(I2C1, I2C_SI);
        } else {
            I2C_SET_CONTROL_REG(I2C1, I2C_STO | I2C_SI);
			i2c1_ctrl_data.flag_finished = 1;
        }
    } else if ((u32Status == 0x30)) {             /* DATA transmitted and NACK received */
        I2C_SET_CONTROL_REG(I2C1, I2C_STO | I2C_SI);
		i2c1_ctrl_data.flag_finished = 1;
    } else {
        /* TO DO */
        //info("Status 0x%x is NOT processed\n", u32Status);
    }
}

static void I2C1_MasterRx(uint32_t u32Status)
{
	if (u32Status == 0x08) {             		/* START transmitted */
        I2C_SET_DATA(I2C1, (i2c1_ctrl_data.addr << 1) | 0x01);
        I2C_SET_CONTROL_REG(I2C1, I2C_SI);
    } else if (u32Status == 0x40) {             /* SLA+R transmitted and ACK received */
		if (i2c1_ctrl_data.rx_data_index < i2c1_ctrl_data.rx_data_length) {
			I2C_SET_CONTROL_REG(I2C1, I2C_SI | I2C_AA);
		} else {
			I2C_SET_CONTROL_REG(I2C1, I2C_STO | I2C_SI);
			i2c1_ctrl_data.flag_finished = 1;
		}
    } else if ((u32Status == 0x48)) {           	/* SLA+R transmitted and NACK received */
        I2C_SET_CONTROL_REG(I2C1, I2C_STO | I2C_SI);
		i2c1_ctrl_data.flag_finished = 1;
    } else if (u32Status == 0x50) {             /* DATA received and ACK returned */
    	i2c1_ctrl_data.rx_data[i2c1_ctrl_data.rx_data_index++] = I2C_GET_DATA(I2C1);
		if (i2c1_ctrl_data.rx_data_index < i2c1_ctrl_data.rx_data_length) {
			I2C_SET_CONTROL_REG(I2C1, I2C_SI | I2C_AA);
		} else {	
       		I2C_SET_CONTROL_REG(I2C1, I2C_STO | I2C_SI);
			i2c1_ctrl_data.flag_finished = 1;
		}
    } else if (u32Status == 0x58) {             /* DATA received and NACK returned */
    	i2c1_ctrl_data.rx_data[i2c1_ctrl_data.rx_data_index++] = I2C_GET_DATA(I2C1);
		I2C_SET_CONTROL_REG(I2C1, I2C_STO | I2C_SI);
		i2c1_ctrl_data.flag_finished = 1;
    } else {
        /* TO DO */
        info("Status 0x%x is NOT processed\n", u32Status);
    }
}

int I2c1_write(int write_bit, uint8_t ic_addr, uint8_t reg_addr, uint8_t reg_val, uint8_t reg_val_1)
{
	int time_out_count;
	
	i2c1_ctrl_data.addr = ic_addr;

	i2c1_ctrl_data.xfer_func = (I2C_FUNC)I2C1_MasterTx;
	i2c1_ctrl_data.tx_data_index = 0;
	if(write_bit == 8){
	  i2c1_ctrl_data.tx_data_length = 2;
	  i2c1_ctrl_data.tx_data[0] = reg_addr;
	  i2c1_ctrl_data.tx_data[1] = reg_val;
	}
	else{
	  i2c1_ctrl_data.tx_data_length = 3;
	  i2c1_ctrl_data.tx_data[0] = reg_addr;
		i2c1_ctrl_data.tx_data[1] = reg_val;
	  i2c1_ctrl_data.tx_data[2] = reg_val_1;
	}
	
	i2c1_ctrl_data.flag_finished = 0;
    I2C_SET_CONTROL_REG(I2C1, I2C_STA);
	time_out_count = 0;
    while ((i2c1_ctrl_data.flag_finished == 0) && (time_out_count++ < 40000)) {
		CLK_SysTickDelay(25);
    };
	//info("i2c1_ctrl_data.flag_finished : %u\n", i2c1_ctrl_data.flag_finished);

	return 0;
};

int I2c1_read(uint8_t ic_addr, uint8_t reg_addr, uint8_t *reg_val)
{
	int time_out_count;
	
	i2c1_ctrl_data.addr = ic_addr;

	i2c1_ctrl_data.xfer_func = (I2C_FUNC)I2C1_MasterTx;
	i2c1_ctrl_data.tx_data_index = 0;
	i2c1_ctrl_data.tx_data_length = 1;
	i2c1_ctrl_data.tx_data[0] = reg_addr;
	
	i2c1_ctrl_data.flag_finished = 0;
    I2C_SET_CONTROL_REG(I2C1, I2C_STA);
	time_out_count = 0;
    while ((i2c1_ctrl_data.flag_finished == 0) && (time_out_count++ < 40000)) {
		CLK_SysTickDelay(25);
    };
	//info("i2c1_ctrl_data.flag_finished : %u\n", i2c1_ctrl_data.flag_finished);

	i2c1_ctrl_data.xfer_func = (I2C_FUNC)I2C1_MasterRx;
	i2c1_ctrl_data.rx_data_index = 0;
	i2c1_ctrl_data.rx_data_length = 1;
	
	i2c1_ctrl_data.flag_finished = 0;
    I2C_SET_CONTROL_REG(I2C1, I2C_STA);
	time_out_count = 0;
    while ((i2c1_ctrl_data.flag_finished == 0) && (time_out_count++ < 40000)) {
		CLK_SysTickDelay(25);
    };
	//info("i2c1_ctrl_data.flag_finished : %u\n", i2c1_ctrl_data.flag_finished);
	*reg_val = i2c1_ctrl_data.rx_data[0];

	return 0;
};

int proximity_sensor_init(void)
{
	uint8_t reg_val;
	
	info("proximity_sensor_init\n");
	
	i2c1_ctrl_data.xfer_func = NULL;
	I2C_Open(I2C1, 100000);
	I2C_EnableInt(I2C1);
    NVIC_EnableIRQ(I2C1_IRQn);

	reg_val = 0;
	I2c1_read(EM30718_I2C_ADDR, EM30718_REG_ADDR_PID, &reg_val);
	if (reg_val != EM30718_PID) {
		is_em30718_exist = 0;
		info("can not find proximity sensor em30718\n");
		return 0;
	} else {
		is_em30718_exist = 1;
		info("proximity sensor id : 0x%02X\n", reg_val);
	}

	I2c1_write(8, EM30718_I2C_ADDR, EM30718_REG_ADDR_CONFIG, 0,0);
	I2c1_write(8, EM30718_I2C_ADDR, EM30718_REG_ADDR_INTERRUPT, 0,0);

	I2c1_write(8, EM30718_I2C_ADDR, EM30718_REG_ADDR_PS_LT, EM30718_PS_LT,0);
	I2c1_write(8, EM30718_I2C_ADDR, EM30718_REG_ADDR_PS_HT, EM30718_PS_HT,0);
	I2c1_write(8, EM30718_I2C_ADDR, EM30718_REG_ADDR_CONFIG,
							(1 << 7) | (1 << 6) | (1 << 3),0);

	GPIO_SetMode(PB, BIT12, GPIO_PMD_INPUT);
	
	return 0;
}

enum proximity_sensor_state proximity_sensor_get_state(void)
{
	uint8_t reg_val;

	if (!is_em30718_exist) {
		return PROXIMITY_SENSOR_STATE_CLOSE;
	}
	I2c1_read(EM30718_I2C_ADDR, EM30718_REG_ADDR_PS_DATA, &reg_val);
	info("EM30718_REG_PS_DATA : %u\n", reg_val);
	if (reg_val >= EM30718_PS_HT) {
		state = PROXIMITY_SENSOR_STATE_CLOSE;
	} else if (reg_val <= EM30718_PS_LT) {
		state = PROXIMITY_SENSOR_STATE_LEAVE;
	}

	return state;
}
